package com.xhwl.centiotdata.suppliersystem.eesd.batown;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.xhwl.centiotdata.dao.ICampusUserStatisticsDao;
import com.xhwl.centiotdata.mq.producer.GeneralProducer;
import com.xhwl.centiotdata.pojo.dto.eesd.EesdConfigDTO;
import com.xhwl.centiotdata.pojo.enums.eesd.EesdMethodEnum;
import com.xhwl.centiotdata.service.cent.IDeviceCentService;
import com.xhwl.centiotdata.service.cent.IEesdCentService;
import com.xhwl.centiotdata.utils.BaAttrsUtil;
import com.xhwl.centiotdata.utils.RedisUtils;
import com.xhwl.centiotdata.utils.ThreadPoolUtils;
import com.xhwl.common.constant.cent.alarm.AlarmConstant;
import com.xhwl.common.dto.cent.device.DeviceDTO;
import com.xhwl.common.dto.cent.energy.EesdElectricDTO;
import com.xhwl.common.dto.cent.energy.EesdElectricDetailDTO;
import com.xhwl.common.enums.device.BADeviceTypeEnum;
import com.xhwl.common.enums.device.BaAttrsEnum;
import com.xhwl.common.enums.device.DeviceTypeEnum;
import com.xhwl.common.pojo.cent.alarm.AlarmManagement;
import com.xhwl.common.pojo.cent.device.Device;
import com.xhwl.common.query.cent.device.DevicePageQuery;
import com.xhwl.common.utils.ChkUtil;
import com.xhwl.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author： lianghz
 * @Description：
 * @Date 2021/09/03/ 09:44
 */
@Service
@Slf4j
public class BaApiHandler {

    @Autowired
    IEesdCentService iEesdCentService;

    @Autowired
    ICampusUserStatisticsDao iCampusUserStatisticsDao;

    @Autowired
    IDeviceCentService iDeviceCentService;

    @Autowired
    GeneralProducer generalProducer;

    @Resource
    private RedisUtils redisUtils;

    /**
     * 拉取电表设备信息
     */
    public List<EesdElectricDTO> pullDevice(EesdConfigDTO configDTO) {
        BaAccessUtil baAccessUtil = new BaAccessUtil(configDTO.getHost(), configDTO.getAppId(), configDTO.getToken(),configDTO.getLoginProjectId());
        Map<String, Object> siteTree = baAccessUtil.getSiteTree();
        if (siteTree == null) {
            log.info("siteTree 是空.");
            return Lists.newArrayList();
        }
        List<Map<String, Object>> data = (List<Map<String, Object>>) siteTree.get("data");
        if (CollectionUtil.isEmpty(data)) {
            log.info("List<Map<String, Object>> data 是空,{}", JSON.toJSONString(siteTree));
            return Lists.newArrayList();
        }
        List<EesdConfigDTO.Electric> electric = configDTO.getElectric();
        List<Map<String, Object>> list = Lists.newArrayList();
        for (EesdConfigDTO.Electric el : electric) {
            handleElectric(data, el.getId(), list);
        }
        if (CollectionUtil.isEmpty(list)) {
            log.info("CollectionUtil.isEmpty(list) 是空,{}", JSON.toJSONString(siteTree));
            return Lists.newArrayList();
        }
        Integer projectId = configDTO.getProjectId();
        List<EesdElectricDTO> arr = Lists.newLinkedList();
        handleElectricArr(list, arr, projectId);
        //使用多线程发起结果
        iEesdCentService.baDeviceSync(arr);
        return arr;
    }

    /**
     * 递归获取电表数组
     */
    private void handleElectricArr(List<Map<String, Object>> data, List<EesdElectricDTO> list, Integer projectId) {
        for (Map<String, Object> datum : data) {
            List<Map<String, Object>> children = (List<Map<String, Object>>) datum.get("Children");
            if (CollectionUtil.isNotEmpty(children)) {
                handleElectricArr(children, list, projectId);
            }
            if ("Unit".equals(datum.get("Type"))) {
                EesdElectricDTO eesdElectricDTO = new EesdElectricDTO();
                eesdElectricDTO.setProductId(EesdMethodEnum.BA_TOWN.getId());
                eesdElectricDTO.setProjectId(projectId);
                eesdElectricDTO.setId((String) datum.get("Id"));
                eesdElectricDTO.setName((String) datum.get("Name"));
                eesdElectricDTO.setCode((String) datum.get("Code"));
                eesdElectricDTO.setType((String) datum.get("Type"));
                eesdElectricDTO.setDeviceType(DeviceTypeEnum.WATT_HOUR_METER.getCode());
                eesdElectricDTO.setChildren(null);
                String name = eesdElectricDTO.getName();
                if (ChkUtil.isNotNull(name)) {
                    if (name.contains("-")) {
                        String[] split = name.split("-");
                        //这里处理了楼栋
                        eesdElectricDTO.setFloorName(split[0] + "号楼");
                        eesdElectricDTO.setAreaName(name);
                    }
                    //这里处理店铺信息
                    if (!name.contains("BY")) {
                        //根据区域名去找店铺
                        if (name.contains("_")) {
                            String[] s = name.split("_");
                            name = s[0];
                        }
                        Map<String, Object> shop = iCampusUserStatisticsDao.findShopNameByRelation(name);
                        if (ChkUtil.isNotNull(shop)) {
                            eesdElectricDTO.setShopId((String) shop.get("shopName"));
                            eesdElectricDTO.setShopType((Integer) shop.get("shopType"));
                        }
                    }
                }
                list.add(eesdElectricDTO);
            }
        }
    }


    /**
     * 递归获取电表数组
     */
    private void handleElectric(List<Map<String, Object>> data, String eId, List<Map<String, Object>> list) {
        for (Map<String, Object> datum : data) {
            List<Map<String, Object>> children = (List<Map<String, Object>>) datum.get("Children");
            if (CollectionUtil.isNotEmpty(children)) {
                handleElectric(children, eId, list);
            }
            if (datum.get("Id").equals(eId)) {
                list.add(datum);
                return;
            }
        }
    }

    /**
     * 拉取电表瞬时电量
     */
    public List<EesdElectricDetailDTO> pullRecord(EesdConfigDTO configDTO) {
        log.info("===============配置参数，{}", configDTO);
        log.info("===============拉取电量数据开始==");
        Date date = new Date();
        List<EesdElectricDTO> eesds = pullDevice(configDTO);
        log.info("===============拉取电量数据的设备数量{}", eesds.size());
        List<EesdElectricDetailDTO> list = Lists.newArrayList();
        for (EesdElectricDTO eesd : eesds) {
            BaAccessUtil baAccessUtil = new BaAccessUtil(configDTO.getHost(), configDTO.getAppId(), configDTO.getToken());
            Map<String, Object> siteTreeDetail = baAccessUtil.getSiteTreeDetail(eesd.getId());
            JSONObject data = (JSONObject) siteTreeDetail.get("data");
            if (CollectionUtil.isEmpty(data)) {
                continue;
            }
            List<JSONObject> attrs = (List<JSONObject>) data.get("attrs");
            for (JSONObject attr : attrs) {
                if ("DB_ZDN".equals(attr.getString("name"))) {
                    EesdElectricDetailDTO da = JSON.toJavaObject(attr, EesdElectricDetailDTO.class);
                    if (da.getValue() != null && ChkUtil.isNotNull(da.getValue().getValue())) {
                        da.setDeviceId(eesd.getId());
                        da.setProjectId(configDTO.getProjectId());
                        da.setDeviceName(eesd.getName());
                        da.setName(eesd.getId());
                        da.setTime(date);
                        da.setDeviceType(DeviceTypeEnum.WATT_HOUR_METER.getCode());
                        list.add(da);
                    }
                }
            }
        }
        log.info("====处理电表数据:{}", list.size());
        if (ChkUtil.isNotNull(list)) {
            iEesdCentService.baDeviceDetailSync(list);
        }
        return list;
    }

    /**
     * 拉取BA设备
     */
    public Object pullBaDevice(EesdConfigDTO eesdConfigDTO) {
        Map<String, Object> siteTree = getMap(eesdConfigDTO);
        if (CollectionUtil.isEmpty(siteTree)) {
            log.info("siteTree 是空.");
            return Lists.newArrayList();
        }
        List<Map<String, Object>> projectData = (List<Map<String, Object>>) siteTree.get("data");
        if (CollectionUtil.isEmpty(projectData)) {
            log.info("List<Map<String, Object>> data 是空.");
            return Lists.newArrayList();
        }
        //顶级-杭州奥特莱斯
        Map<String, Object> data = projectData.get(0);
        List<Map<String, Object>> list = Lists.newArrayList();
        handleBa((List<Map<String, Object>>) data.get("Children"), list);
        //入库
        Date current = new Date();
        List<DeviceDTO> deviceDTOList = Lists.newArrayList();
        for (Map<String, Object> map : list) {
            DeviceDTO query = new DeviceDTO();
            String deviceId = ChkUtil.getString(map.get("Id"));
            String deviceName = ChkUtil.getString(map.get("Name"));
            String type = ChkUtil.getString(map.get("Type"));
            if (!"Unit".equals(type)) {
                log.error("过滤设备:{}", map);
                continue;
            }
            //在离线需要去详情里面获取
            int isOnline = 0;
            try {
                isOnline = getDetail(eesdConfigDTO, deviceId);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //含有“传感器”字段：环境设备
            //含有“泛光”、“户外”、"花园"的字段：照明设备
            //含有“集水井”的字段：集水井
            //含有“CO”的字段：一氧化碳
            //其他：排风机组
            /**
             * 根据枚举关键字，获取设备类型id
             */
            BADeviceTypeEnum anEnum = BADeviceTypeEnum.getEnum(deviceName);
            query.setDeviceTypeId(anEnum.getTypeId());

            query.setProjectId(eesdConfigDTO.getProjectId());
            query.setThirdDeviceId(deviceId);
            query.setName(deviceName);
            query.setCategoryId(8);
            query.setDeviceStatus(1);
            query.setProductId(EesdMethodEnum.BA_TOWN.getId());
            query.setOnlineStatus(isOnline);
            query.setAttributeJson(JSON.parseObject(JSON.toJSONString(map)));
            query.setCreatedAt(current);
            deviceDTOList.add(query);
        }
        Map<Integer, List<DeviceDTO>> collect = deviceDTOList.stream().collect(Collectors.groupingBy(DeviceDTO::getDeviceTypeId));
        collect.forEach((k, v) -> {
            if (!CollectionUtil.isEmpty(v)) {
                iDeviceCentService.asyncUpdateDevice(v);
            }
        });
        return deviceDTOList;
    }

    /**
     * 递归获取BA数组
     */
    private void handleBa(List<Map<String, Object>> data, List<Map<String, Object>> list) {
        for (Map<String, Object> datum : data) {
            List<Map<String, Object>> children = (List<Map<String, Object>>) datum.get("Children");
            if (CollectionUtil.isNotEmpty(children)) {
                handleBa(children, list);
            }
            datum.remove("Children");
            list.add(datum);
        }
    }

    /**
     * 获取详情
     */
    private static int getDetail(EesdConfigDTO eesdConfigDTO, String baId) {
        Map<String, Object> siteTree;
        String os = System.getProperty("os.name");
        if (os.toLowerCase().startsWith("win")) {
            //拉取BA设备
            String content = getContent("ba-detail.json");
            siteTree = JSON.parseObject(content, Map.class);
        } else {
            BaAccessUtil baAccessUtil = new BaAccessUtil(eesdConfigDTO.getBahost().getHost(), eesdConfigDTO.getBahost().getAppId(), eesdConfigDTO.getBahost().getToken());
            siteTree = baAccessUtil.getSiteTreeDetail(baId);
        }

        if (ChkUtil.isNotNull(siteTree)) {
            Map detailData = (Map) siteTree.get("data");
            if (ChkUtil.isNotNull(detailData)) {
                List<JSONObject> attrs = (List<JSONObject>) detailData.get("attrs");
                for (JSONObject attr : attrs) {
                    if ("device".equals(attr.getString("name"))) {
                        JSONObject object = attr.getJSONObject("value");
                        Boolean value = object.getBoolean("Value");
                        log.info("{}在线状态:{}", baId, value);
                        return value ? 1 : 0;
                    }
                }
            }
        }
        return 0;
    }

    /**
     * 获取数据
     */
    private static Map<String, Object> getMap(EesdConfigDTO eesdConfigDTO) {
        Map<String, Object> siteTree;
        String os = System.getProperty("os.name");
        if (os.toLowerCase().startsWith("win")) {
            //拉取BA设备
            String content = getContent("ba-device.json");
            siteTree = JSON.parseObject(content, Map.class);
        } else {
            BaAccessUtil baAccessUtil = new BaAccessUtil(eesdConfigDTO.getBahost().getHost(), eesdConfigDTO.getBahost().getAppId(), eesdConfigDTO.getBahost().getToken(),eesdConfigDTO.getLoginProjectId());
            siteTree = baAccessUtil.getSiteTree();
        }
        return siteTree;
    }

    /**
     * 获取文件
     */
    private static String getContent(String fileName) {
        String res = "";
        if (ChkUtil.isNull(fileName)) {
            log.info("文件路径不能为空");
            return res;
        }
        try {
            ClassPathResource resource = new ClassPathResource(fileName);
            BufferedReader br = new BufferedReader(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            String str;
            while ((str = br.readLine()) != null) {
                sb.append(str);
            }
            res = sb.toString();
        } catch (Exception e) {
            log.info("读取文件{}时发生异常", fileName);
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 拉取设备详情检查是否有告警
     *
     * @param eesdConfigDTO
     * @param thirdDeviceId
     * @param deviceId      设备在device表中的id
     */
    public void pullDetailCheckAlarm(EesdConfigDTO eesdConfigDTO, String thirdDeviceId, Integer deviceId) {
        try {
            BaAccessUtil baAccessUtil = new BaAccessUtil(eesdConfigDTO.getBahost().getHost(), eesdConfigDTO.getBahost().getAppId(), eesdConfigDTO.getBahost().getToken());
            Map<String, Object> siteTreeDetail = baAccessUtil.getSiteTreeDetail(thirdDeviceId);
            Map<BaAttrsEnum, Boolean> baAttrs = BaAttrsUtil.getBaAttrs(siteTreeDetail.toString());
            if(ObjectUtils.isEmpty(redisUtils.get(deviceId + "_status"))){
                redisUtils.set(deviceId + "_status",baAttrs.get(BaAttrsEnum.device));
            }else if (!(boolean)redisUtils.get(deviceId + "_status") && baAttrs.get(BaAttrsEnum.device)){
                redisUtils.set(deviceId + "_status",baAttrs.get(BaAttrsEnum.device));
            }

            BaAttrsUtil baAttrsUtil = new BaAttrsUtil(baAttrs);
            //高液位告警
            if (baAttrsUtil.checkAttr(true, BaAttrsEnum.GYW)) {
                sendAlarm(eesdConfigDTO.getProjectId(), deviceId, BaAttrsEnum.GYW.getAlarmCode());
            }
            //离线告警
            if ((boolean)redisUtils.get(deviceId + "_status") && !baAttrs.get(BaAttrsEnum.device)) {
                redisUtils.set(deviceId + "_status",false);
                sendAlarm(eesdConfigDTO.getProjectId(), deviceId, BaAttrsEnum.device.getAlarmCode());
            }
            //故障告警
            if (baAttrsUtil.checkAttr(true, BaAttrsEnum.GZ, BaAttrsEnum.GZ2, BaAttrsEnum.FAULT)) {
                sendAlarm(eesdConfigDTO.getProjectId(), deviceId, BaAttrsEnum.FAULT.getAlarmCode());
            }
        } catch (Exception e) {
//            不输出全日志，在ba设备请求异常时，这部分设备比较多，日志非常占用内存
//            log.error("拉取设备详情检查是否有告警异常", e);
            log.error("拉取设备详情检查是否有告警异常", e.getMessage());
        }
    }

    /**
     * 拉取状态信息，生成告警
     */
    public void pullDevicesCheckAlarm(EesdConfigDTO eesdConfigDTO) {
        if (eesdConfigDTO == null)
            return;
        DevicePageQuery deviceQuery = new DevicePageQuery();
        deviceQuery.setProjectId(eesdConfigDTO.getProjectId());
        deviceQuery.setDeviceTypeIdList(BADeviceTypeEnum.getTypeIds());
        log.info("========================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        log.info("eesdConfigDTO=" + JSON.toJSONString(eesdConfigDTO));
        List<DeviceDTO> deviceList = iDeviceCentService.listDevice(null, deviceQuery);

        //分批请求
        List<List<DeviceDTO>> partition = Lists.partition(deviceList, 50);
        log.info("告警请求，分批次数：" + partition.size());
        for (int i = 0; i < partition.size(); i++) {
            try {
                Thread.sleep(i * 500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            List<DeviceDTO> deviceDTOS = partition.get(i);
            for (DeviceDTO deviceDTO : deviceDTOS) {
                String thirdDeviceId = deviceDTO.getThirdDeviceId();
                if (StringUtils.isNotEmpty(thirdDeviceId)) {
                    ThreadPoolUtils.alarmExecutor.execute(() -> pullDetailCheckAlarm(eesdConfigDTO, thirdDeviceId, deviceDTO.getId()));
                }
            }
        }
    }

    private static final String TOPIC = AlarmConstant.ROMA_TOPIC;

    /**
     * 发送告警
     *
     * @param projectId
     * @param deviceId  中台设备id
     * @param alarmCode 告警code
     */
    private void sendAlarm(Integer projectId, Integer deviceId, String alarmCode) {

        AlarmManagement alarmManagement = new AlarmManagement();
        //组装公共部分数据
        alarmManagement.setProjectId(projectId);
        alarmManagement.setAlarmTime(new Date());
        alarmManagement.setDeviceId(deviceId);
        alarmManagement.setAlarmInventoryCode(alarmCode);
        log.info("发出告警: {}", JSON.toJSONString(alarmManagement));
        generalProducer.send(TOPIC, alarmManagement);
    }
}
